#ifndef SELECTION_H
#define SELECTION_H


#include <vector>


#ifdef SCI_NAMESPACE
namespace Scintilla {
#endif

  class SelectionPosition {
      int position;
      int virtualSpace;
    public:
      explicit SelectionPosition( int position_ = INVALID_POSITION, int virtualSpace_ = 0 ) : position( position_ ), virtualSpace( virtualSpace_ ) {
        PLATFORM_ASSERT( virtualSpace < 800000 );
        if( virtualSpace < 0 ) {
          virtualSpace = 0;
        }
      }
      void Reset() {
        position = 0;
        virtualSpace = 0;
      }
      void MoveForInsertDelete( bool insertion, int startChange, int length );
      bool operator ==( const SelectionPosition &other ) const {
        return position == other.position && virtualSpace == other.virtualSpace;
      }
      bool operator <( const SelectionPosition &other ) const;
      bool operator >( const SelectionPosition &other ) const;
      bool operator <=( const SelectionPosition &other ) const;
      bool operator >=( const SelectionPosition &other ) const;
      int Position() const {
        return position;
      }
      void SetPosition( int position_ ) {
        position = position_;
        virtualSpace = 0;
      }
      int VirtualSpace() const {
        return virtualSpace;
      }
      void SetVirtualSpace( int virtualSpace_ ) {
        PLATFORM_ASSERT( virtualSpace_ < 800000 );
        if( virtualSpace_ >= 0 ) {
          virtualSpace = virtualSpace_;
        }
      }
      void Add( int increment ) {
        position = position + increment;
      }
      bool IsValid() const {
        return position >= 0;
      }
  };

  // Ordered range to make drawing simpler
  struct SelectionSegment {
    SelectionPosition start;
    SelectionPosition end;
    SelectionSegment() : start(), end() {
    }
    SelectionSegment( SelectionPosition a, SelectionPosition b ) {
      if( a < b ) {
        start = a;
        end = b;
      } else {
        start = b;
        end = a;
      }
    }
    bool Empty() const {
      return start == end;
    }
    void Extend( SelectionPosition p ) {
      if( start > p ) {
        start = p;
      }
      if( end < p ) {
        end = p;
      }
    }
  };

  struct SelectionRange {
    SelectionPosition caret;
    SelectionPosition anchor;

    SelectionRange() : caret(), anchor() {
    }
    explicit SelectionRange( SelectionPosition single ) : caret( single ), anchor( single ) {
    }
    explicit SelectionRange( int single ) : caret( single ), anchor( single ) {
    }
    SelectionRange( SelectionPosition caret_, SelectionPosition anchor_ ) : caret( caret_ ), anchor( anchor_ ) {
    }
    SelectionRange( int caret_, int anchor_ ) : caret( caret_ ), anchor( anchor_ ) {
    }
    bool Empty() const {
      return anchor == caret;
    }
    int Length() const;
    // int Width() const; // Like Length but takes virtual space into account
    bool operator ==( const SelectionRange &other ) const {
      return caret == other.caret && anchor == other.anchor;
    }
    bool operator <( const SelectionRange &other ) const {
      return caret < other.caret || ( ( caret == other.caret ) && ( anchor < other.anchor ) );
    }
    void Reset() {
      anchor.Reset();
      caret.Reset();
    }
    void ClearVirtualSpace() {
      anchor.SetVirtualSpace( 0 );
      caret.SetVirtualSpace( 0 );
    }
    void MoveForInsertDelete( bool insertion, int startChange, int length );
    bool Contains( int pos ) const;
    bool Contains( SelectionPosition sp ) const;
    bool ContainsCharacter( int posCharacter ) const;
    SelectionSegment Intersect( SelectionSegment check ) const;
    SelectionPosition Start() const {
      return ( anchor < caret ) ? anchor : caret;
    }
    SelectionPosition End() const {
      return ( anchor < caret ) ? caret : anchor;
    }
    bool Trim( SelectionRange range );
    // If range is all virtual collapse to start of virtual space
    void MinimizeVirtualSpace();
  };

  class Selection {
      std::vector<SelectionRange> ranges;
      std::vector<SelectionRange> rangesSaved;
      SelectionRange rangeRectangular;
      size_t mainRange;
      bool moveExtends;
      bool tentativeMain;
    public:
      enum selTypes { noSel, selStream, selRectangle, selLines, selThin };
      selTypes selType;

      Selection();
      ~Selection();
      bool IsRectangular() const;
      int MainCaret() const;
      int MainAnchor() const;
      SelectionRange &Rectangular();
      SelectionSegment Limits() const;
      // This is for when you want to move the caret in response to a
      // user direction command - for rectangular selections, use the range
      // that covers all selected text otherwise return the main selection.
      SelectionSegment LimitsForRectangularElseMain() const;
      size_t Count() const;
      size_t Main() const;
      void SetMain( size_t r );
      SelectionRange &Range( size_t r );
      const SelectionRange &Range( size_t r ) const;
      SelectionRange &RangeMain();
      const SelectionRange &RangeMain() const;
      SelectionPosition Start() const;
      bool MoveExtends() const;
      void SetMoveExtends( bool moveExtends_ );
      bool Empty() const;
      SelectionPosition Last() const;
      int Length() const;
      void MovePositions( bool insertion, int startChange, int length );
      void TrimSelection( SelectionRange range );
      void SetSelection( SelectionRange range );
      void AddSelection( SelectionRange range );
      void AddSelectionWithoutTrim( SelectionRange range );
      void DropSelection( size_t r );
      void TentativeSelection( SelectionRange range );
      void CommitTentative();
      int CharacterInSelection( int posCharacter ) const;
      int InSelectionForEOL( int pos ) const;
      int VirtualSpaceFor( int pos ) const;
      void Clear();
      void RemoveDuplicates();
      void RotateMain();
      bool Tentative() const { return tentativeMain; }
      std::vector<SelectionRange> RangesCopy() const {
        return ranges;
      }
  };

  #ifdef SCI_NAMESPACE
}
  #endif

#endif
